package vn.wallet.app.managedBean;

import java.io.Serializable;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;

import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.ViewScoped;

import org.apache.log4j.Logger;
import org.primefaces.context.RequestContext;
import org.primefaces.event.NodeSelectEvent;
import org.primefaces.model.DefaultTreeNode;
import org.primefaces.model.TreeNode;

import vn.wallet.app.domain.Category;
import vn.wallet.app.helper.PageUtils;
import vn.wallet.app.helper.exceptions.TechnicalException;
import vn.wallet.app.service.ICategoryService;

@ManagedBean(name = "categoryBean")
@ViewScoped
public class CategoryBean extends DefaultBean implements Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private final Logger log = Logger.getLogger(this.getClass());

	@ManagedProperty(value = "#{categoryService}")
	private ICategoryService categoryService;
	private Category customCategory;

	private boolean updateMode = false;

	private TreeNode rootNode;
	private TreeNode selectedNode;
	private List<Category> lstCategory;
	private final HashMap<Integer, TreeNode> mapCategory;
	private final int OTHER_CATEGORY = 1;
	private int selectedId;

	public CategoryBean() {
		initNewCategory();

		// init tree mode
		mapCategory = new HashMap<Integer, TreeNode>();
		mapCategory.put(0, null);
	}

	@Override
	@PostConstruct
	public void prepareData() {
		reloadCategory();
	}

	@Override
	protected void setPageTitle(String str) {
		super.siteConfig.setSiteTitle("Category Bean");
	}

	private void reloadCategory() {
		try {
			rootNode = new DefaultTreeNode("Root", null);
			lstCategory = categoryService.getAllByUserId(currentUser.getId());
			// for (Category cat : lstCategory) {
			// System.out.println("cat [" + cat.getParentId() + "][" +
			// cat.getId() + "] : " + cat.getName());
			// }

			sortCategoryList(lstCategory);

			int beforeId = 0;
			TreeNode currentNode = rootNode;

			for (Category cat : lstCategory) {
				if (cat.getParentId() != beforeId) {
					currentNode = (mapCategory.get(cat.getParentId()) == null) ? rootNode : mapCategory.get(cat.getParentId());
					beforeId = cat.getParentId();
				}
				DefaultTreeNode node = new DefaultTreeNode(cat, currentNode);

				mapCategory.put(cat.getId(), node);
			}

			setUpdateMode(false);
			initNewCategory();
		} catch (Exception ex) {
			throw new TechnicalException("Error when loading category", ex);
		}
	}

	private void sortCategoryList(List<Category> lstCategory) {
		Collections.sort(lstCategory, new Comparator<Category>() {

			@Override
			public int compare(Category c1, Category c2) {
				if (c1.getParentId() == c2.getParentId()) {
					return c1.getName().compareTo(c2.getName());
				} else {
					return (c1.getParentId() - c2.getParentId());
				}
			}
		});
		// put other to level 1
		for (int i = 0; i < lstCategory.size(); i++) {
			Category temp = lstCategory.get(i);
			if (temp.getId() == OTHER_CATEGORY) {
				lstCategory.remove(i);
				lstCategory.add(lstCategory.size(), temp);
				break;
			}
		}
	}

	public void nodeSelected(NodeSelectEvent event) {
		TreeNode selectedNode = event.getTreeNode();
		customCategory = (Category) selectedNode.getData();
		setSelectedId(customCategory.getParentId());
		setUpdateMode(true);
	}

	private void initNewCategory() {
		customCategory = new Category(getCurrentUser());
	}

	/**
	 * if modeId = 0: init new category item<br/>
	 * else update selectedNode by form value
	 * 
	 * @param modeID
	 */
	public void prepareItem(long modeID) {
		// System.out.println("prepareItem");
		if (modeID == 1) { // edit mode
			if (updateMode) {
				updateItem();
			} else {
				saveItem();
			}
		}
		reloadCategory();
		setSelectedId(0);
		RequestContext.getCurrentInstance().update("form_update");
		RequestContext.getCurrentInstance().update("form_action");
		RequestContext.getCurrentInstance().update("form_main_top");
	}

	@Override
	public void saveItem() {
		try {
			if (validateItem()) {
				customCategory.setParentId(getSelectedId());
				categoryService.save(customCategory);
				PageUtils.logSuccess(PageUtils.FUNC_NAME.ADD, "");
				log.info("Added new category: " + customCategory.getName());
			}
		} catch (Exception ex) {
			ex.printStackTrace();
			PageUtils.logFailed(PageUtils.FUNC_NAME.ADD, "");
		}
	}

	@Override
	public void updateItem() {
		try {
			if (validateItem()) {
				categoryService.update(customCategory);
				PageUtils.logSuccess(PageUtils.FUNC_NAME.EDIT, "");
				log.info("Added new category: " + customCategory.getName());
			}
		} catch (Exception ex) {
			ex.printStackTrace();
			PageUtils.logFailed(PageUtils.FUNC_NAME.EDIT, "");
		}
	}

	@Override
	public void deleteItem() {
		try {
			categoryService.delete(customCategory);
			PageUtils.logSuccess(PageUtils.FUNC_NAME.DELETE, "");
			log.info("Added new category: " + customCategory.getName());
			prepareItem(0);
		} catch (Exception ex) {
			ex.printStackTrace();
			PageUtils.logFailed(PageUtils.FUNC_NAME.DELETE, "");
		}
	}

	private boolean validateItem() {
		boolean ret = false;
		customCategory.setParentId(getSelectedId());
		if (!categoryService.isExistingCategory(customCategory)) {
			ret = true;
		} else {
			ret = false;
			PageUtils.logFailed("Duplicate category name");
		}

		return ret;
	}

	// ----------------------------------getter & setter
	public Category getCustomCategory() {
		return customCategory;
	}

	public void setCustomCategory(Category customCategory) {
		this.customCategory = customCategory;
	}

	public ICategoryService getCategoryService() {
		return categoryService;
	}

	public void setCategoryService(ICategoryService categoryService) {
		this.categoryService = categoryService;
	}

	public TreeNode getRootNode() {
		// if (rootNode != null) {
		// System.out.println("node: " + getNodeChild("", rootNode));
		// }
		return rootNode;
	}

	private String getNodeChild(String str, TreeNode nodeParent) {
		String ret = "";
		List<TreeNode> lstChildren = nodeParent.getChildren();
		for (TreeNode node : lstChildren) {
			Category cat = (Category) node.getData();
			ret += cat.getName() + ", ";

			if (node.getChildren().size() > 0) {
				ret += "[" + getNodeChild("", node) + "]";
			}
		}
		ret += "";
		return ret;
	}

	public void setRootNode(TreeNode rootNode) {
		this.rootNode = rootNode;
	}

	public TreeNode getSelectedNode() {
		return selectedNode;
	}

	public void setSelectedNode(TreeNode selectedNode) {
		this.selectedNode = selectedNode;
	}

	public List<Category> getLstCategory() {
		return lstCategory;
	}

	public void setLstCategory(List<Category> lstCategory) {
		this.lstCategory = lstCategory;
	}

	public int getSelectedId() {
		return selectedId;
	}

	public void setSelectedId(int selectedId) {
		setUpdateMode(selectedId != 0);
		this.selectedId = selectedId;
	}

	public boolean isUpdateMode() {
		return updateMode;
	}

	public void setUpdateMode(boolean updateMode) {
		this.updateMode = updateMode;
	}

}
